package com.hiof.activities;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.dsi.ant.AntInterface;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.hiof.Configuration;
import com.hiof.R;
import com.hiof.antplus.ANTPlusService;
import com.hiof.antplus.AntPlusManager;
import com.hiof.antplus.PairingDialog;
import com.hiof.beans.UserInfo;
import com.hiof.connection.observers.BluetoothConnectionManager;
import com.hiof.data.MoraService;
import com.hiof.location.GPSManager;
import com.hiof.location.GpsMangerFactory;
import com.hiof.location.earcon.EncourageNotificationManager;
import com.hiof.media.CameraIntent;
import com.hiof.media.MediaFilesObserver;
import com.hiof.media.MediaIntent;
import com.hiof.menus.NavigationMenu;
import com.hiof.overlays.*;
import com.hiof.servercommunication.RestFulServiceManager;
import com.hiof.servercommunication.background.job.AthleteHeartRateSendJob;
import com.hiof.servercommunication.background.job.JobThreadQueue;
import com.hiof.servercommunication.background.job.TimerJobManager;
import com.hiof.servercommunication.background.job.managers.AbstractBackgroundThreadManager;
import com.hiof.servercommunication.background.job.managers.BackgroundManagerFactory;
import com.hiof.servercommunication.receive.beans.GeoPoint;
import com.hiof.settings.SettingManager;
import com.hiof.settings.user.TypeOfAttendant;
import com.hiof.settings.user.UserConfiguration;
import com.hiof.utility.Utility;
import com.hiof.voiceguide.Speaker;

public class MainActivityAthlete extends MapActivity implements AntPlusManager.Callbacks {

    /*
    * TODO: Implement the activity like first login, then select route and then show this activity with buttons
    * use singleton and factory method for that and strategy pattern for text sound and file sound play
    */
    MediaFilesObserver mediaFilesObserver;
    private MapView mapView;
    private MapController mapController;
    private OwnOverlay ownOverlay;
    private TeammatePositionOverlay teammatePositionOverlay;
    private AthletePositionOverlay athletePositionOverlay;
    private boolean antSupport;

    private long tempMessageSentTime = 0;
    private long tempMessageSavedTime = 0;
    private int tempHeartRate = 0;


    /**
     * Class to manage all the ANT messaging and setup
     */
    private AntPlusManager mAntManager;

    private boolean mBound;

    private PairingDialog mShownDialog = null;
    private static final int AGE = 46;
    private static final long SEND_SMS_TIME_INTERVAL = 600000;
    private static final long SAVE_HRM_TIME_INTERVAL = 600000;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Configuration.getInstance().setCurrentContext(this);
        antSupport = AntInterface.hasAntSupport(this);
        setContentView(R.layout.main);
//        setUpUi();
        initLocationFunctionality();
        drawRaceRoute();
        setUpJobThread();

        BluetoothConnectionManager.getInstance().open();

        Speaker.initialize(this);
        startMediaObserver();

        Utility.debug(this.getClass().getName(), "Activity lifecycle: Start Main Activity ");
    }

    @Override
    protected void onStart() {
        if (antSupport && UserConfiguration.getInstance().getTypeOfAttendant() == TypeOfAttendant.ATHLETE) {
            mBound = bindService(new Intent(this, ANTPlusService.class),
                    mConnection, BIND_AUTO_CREATE);
        } else {
            showAntNotSupported();
        }
        super.onStart();
    }

    private void showAntNotSupported() {
        Toast.makeText(this, "Ant+ service is not available", Toast.LENGTH_SHORT).show();
    }

    private void initLocationFunctionality() {
        //initialize Location updates
        GPSManager gpsManager = GpsMangerFactory.getGpsManager();
        gpsManager.registerListener(EncourageNotificationManager.getInstance());

        mapView = (MapView) findViewById(R.id.mapview);
        mapController = mapView.getController();
        mapView.getController().setZoom(15);
        mapView.setBuiltInZoomControls(false);
        GeoPoint initGeoPoint = gpsManager.getLatestLocation();

        ownOverlay = new OwnOverlay(getResources(), initGeoPoint);
        gpsManager.registerListener(ownOverlay);
        /*
            teammatePositionOverlay is registered in the jobthread manager
            as it has to listen the location updates from the server
         */
        teammatePositionOverlay = new TeammatePositionOverlay(getResources());
        athletePositionOverlay = new AthletePositionOverlay(getResources());

        mapView.getOverlays().add(ownOverlay);
        mapView.getOverlays().add(teammatePositionOverlay);
        mapView.getOverlays().add(athletePositionOverlay);
        mapController.animateTo(initGeoPoint.getGoogleGeoPoint());
    }

    private void setUpJobThread() {
        //start job thread
        JobThreadQueue jobThreadQueue = JobThreadQueue.getInstance();
        jobThreadQueue.startThread();
        AbstractBackgroundThreadManager threadManager = BackgroundManagerFactory.getBackgroundManager(TypeOfAttendant.ATHLETE);
        threadManager.setUp();
        //TODO: register observer here
        threadManager.registerListener(MoraService.getInstance());
        //TODO: if the user is athlete register teammatesoverlay
        threadManager.registerListener(teammatePositionOverlay);
    }

    private void drawRaceRoute() {
        TripRouteOverlay tripRouteOverlay = new TripRouteOverlay(this);
        mapView.getOverlays().add(tripRouteOverlay);
    }

    private void startMediaObserver() {
        mediaFilesObserver = MediaFilesObserver.getInstance();
        mediaFilesObserver.startWatching();
    }

    @Override
    protected void onPause() {
        super.onPause();
//        BluetoothConnectionManager.getInstance().destroy();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!BluetoothConnectionManager.getInstance().isConnected()) {
            BluetoothConnectionManager.getInstance().open();
        }
    }


    @Override
    public void onBackPressed() {
    }

    public void onMenuButtonClick(View v) {
        int key = v.getId();
        switch (key) {
            case R.id.cancel_button:
                NavigationMenu.getInstance().cancel();
                break;
            case R.id.select_button:
                NavigationMenu.getInstance().select();
                break;
            case R.id.next_button:
                NavigationMenu.getInstance().next();
                break;
            case R.id.previous_button:
                NavigationMenu.getInstance().previous();
                break;
            case R.id.camera_button:
                MediaIntent camera = new CameraIntent();
                Configuration.getInstance().getCurrentContext().startActivity(camera);
                break;
            case R.id.btn_enable_ant:
                if (antSupport) {
                    if (!mAntManager.isChannelOpen(AntPlusManager.HRM_CHANNEL)) {
                        Utility.debug(this.getClass().getName(), "onClick (HRM): Open channel");
                        mAntManager.openChannel(AntPlusManager.HRM_CHANNEL, false);
//                    ((Button)findViewById(R.id.btn_enable_ant)).setText(getString(R.string.Disable_Ant));
                    } else {
                        Utility.debug(this.getClass().getName(), "onClick (HRM): Close channel");
                        mAntManager.closeChannel(AntPlusManager.HRM_CHANNEL);
                        ((Button) findViewById(R.id.btn_enable_ant)).setText(getString(R.string.Enable_Ant));
                    }
                }
                break;
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        destroyServices();
    }

    @Override
    protected void onStop() {
        if (antSupport) {
            if (mAntManager != null) {
                saveState();
                mAntManager.setCallbacks(null);
            }
            if (mBound) {
                unbindService(mConnection);
            }
        }
        super.onStop();
    }

    @Override
    protected boolean isRouteDisplayed() {
        return true;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.map_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.setting:
                showPreferences();
                break;
            case R.id.signout:
                signOut();
                break;
            case R.id.exit:
                exit();
            default:
                return super.onOptionsItemSelected(item);
        }
        return true;
    }

    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            // This is very unlikely to happen with a local service (ie. one in
            // the same process)
            if (antSupport) {
                mAntManager.setCallbacks(null);
                mAntManager = null;
            }
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (antSupport) {
                mAntManager = ((ANTPlusService.LocalBinder) service).getManager();
                mAntManager.setCallbacks(MainActivityAthlete.this);
                loadConfiguration();


                if (mShownDialog != null) {
                    mShownDialog.setEnabled(true);
                    switch (mShownDialog.getId()) {
                        case PairingDialog.HRM_ID:
                            mShownDialog.setDeviceNumber(mAntManager
                                    .getDeviceNumberHRM());
                            break;
                        case PairingDialog.BUFF_ID:
                            mShownDialog.setDeviceNumber(mAntManager
                                    .getBufferThreshold());
                            break;
                        case PairingDialog.PROX_ID:
                            mShownDialog.setProximityThreshold(mAntManager
                                    .getProximityThreshold());
                            break;
                        default:
                            // Other ID's don't matter
                            break;
                    }
                }
                init();
                notifyAntStateChanged();
            }
        }
    };

    private void receiveOrUpdateHeartRateInfo(byte channel) {
        if (channel == AntPlusManager.HRM_CHANNEL) {
            switch (mAntManager.getHrmState()) {
                case CLOSED:
                case OFFLINE:
                case SEARCHING:
                case PENDING_OPEN:
                    Utility.debug(this.getClass().getName(), "notifyChannelDataChanged pending--:");
                    ((Button) findViewById(R.id.btn_enable_ant)).setText("Enable Ant");
                    break;
                case TRACKING_STATUS:
                    // There is no Status state for the HRM channel, so we will
                    // attempt to show latest data instead
                case TRACKING_DATA:
                    Utility.debug(this.getClass().getName(), "notifyChannelDataChanged value:" + mAntManager.getBPM());
                    handleHearRateData();
                    break;
            }
        }
    }

    private void handleHearRateData() {
        ((Button) findViewById(R.id.btn_enable_ant)).setText(String.valueOf(mAntManager.getBPM() + " bpm"));
    }

    private void init() {
        if (!mAntManager.isChannelOpen(AntPlusManager.HRM_CHANNEL)) {
            if (!mAntManager.isEnabled()) {
                mAntManager.doEnable();
                /*  Message msg = new Message();
                Bundle bundle = new Bundle();
                msg.setData(bundle);
                bundle.putString("message","ant enabled");
                antConnectionHandler.sendMessage(msg);*/
            }

        }
    }

    /**
     * Store application persistent data.
     */
    private void saveState() {
        SettingManager.getInstance().setAntHRMDeviceNumber(mAntManager.getDeviceNumberHRM());
        SettingManager.getInstance().setProximityThreshold(mAntManager.getProximityThreshold());
        SettingManager.getInstance().setProximityThreshold(mAntManager.getBufferThreshold());
    }

    /**
     * Retrieve application persistent data.
     */
    private void loadConfiguration() {
        // Restore preferences
        mAntManager.setDeviceNumberHRM((short) SettingManager.getInstance().getAntHRMDeviceNumber());
        mAntManager.setProximityThreshold((byte) SettingManager.getInstance().getProximityThreshold());
        mAntManager.setBufferThreshold((short) SettingManager.getInstance().getBufferThreshold());
    }


    private void showPreferences() {
        startActivity(new Intent(this, Preferences.class));
    }

    private void exit() {
        JobThreadQueue.getInstance().clearJobQueue();
        JobThreadQueue.getInstance().stopThread();
        unregisteredObservers();
        destroyServices();
        finish();
    }

    private void destroyServices() {
        RestFulServiceManager.getInstance().shutDownService();

        Configuration.getInstance().destroy();
        BluetoothConnectionManager.getInstance().destroy();
        GpsMangerFactory.getGpsManager().destroy();
        EncourageNotificationManager.getInstance().destroy();
        MediaFilesObserver.getInstance().destroy();
        NavigationMenu.getInstance().destroy();
        SettingManager.getInstance().destroy();
        MapPointsManager.getInstane().destroy();

        UserConfiguration.getInstance().destroy();
        MoraService.getInstance().destroy();
        Speaker.stop();
    }

    private void unregisteredObservers() {
        AbstractBackgroundThreadManager threadManager = BackgroundManagerFactory.getBackgroundManager(TypeOfAttendant.ATHLETE);
        threadManager.removeListener(MoraService.getInstance());


        GpsMangerFactory.getGpsManager().removeListener(ownOverlay);
        GpsMangerFactory.getGpsManager().removeListener(EncourageNotificationManager.getInstance());
        mediaFilesObserver.stopWatching();
    }

    private void signOut() {
        SettingManager.getInstance().setUsername("");
        SettingManager.getInstance().setPassword("");
        TimerJobManager.getInstance().stopTimerJob();
        exit();
    }

    // Implementations of the AntPlusManager call backs
    @Override
    public void errorCallback() {
        mAntManager.clearChannelStates();
    }

    @Override
    public void notifyAntStateChanged() {
        boolean showChannels = mAntManager.checkAntState();
        if (!showChannels) {
            mAntManager.clearChannelStates();
        } else {
            receiveOrUpdateHeartRateInfo(AntPlusManager.HRM_CHANNEL);
        }
    }

    @Override
    public void notifyChannelStateChanged(byte channel) {
        receiveOrUpdateHeartRateInfo(channel);
    }

    @Override
    public void notifyChannelDataChanged(byte channel) {
//        TODO: send sms when it is above threshold value
        receiveOrUpdateHeartRateInfo(channel);
        checkAndNotifyMaxHeartRate();
        saveAndSendHeartRateData();
    }


    private void saveAndSendHeartRateData() {
//        check if heart rate measurement is being done at least 10 min before and the previous heart rate and current heart rate are not same
        if (((System.currentTimeMillis() - tempMessageSavedTime) > SAVE_HRM_TIME_INTERVAL) && tempHeartRate != mAntManager.getBPM()) {
            saveHeartRateData();
//                send data to server
            sendHeartRateToServer();
            tempHeartRate = mAntManager.getBPM();
        }
    }

    private void saveHeartRateData() {
        MoraService.getInstance().saveHeartRateValue(UserConfiguration.getInstance().getAttendantId(), mAntManager.getBPM(), "Cycling");
        SettingManager.setCurrentHeartRate(mAntManager.getBPM());
    }

    private void sendHeartRateToServer() {
        JobThreadQueue.getInstance().addJob(new AthleteHeartRateSendJob());
    }

    private void checkAndNotifyMaxHeartRate() {
        //        send sms TODO: put the right max heart rate and temp time i.e. 10 mins
        /*
            Male athletes - MHR = 202 - (0.55 x age)
            Female athletes - MHR = 216 - (1.09 x age)
         */
        try {
            int maxHeartRate = 60;//(int) (202-(0.55*AGE));
            if (mAntManager.getBPM() >= maxHeartRate) {
                ((Button) findViewById(R.id.btn_enable_ant)).setBackgroundColor(Color.RED);
                if ((System.currentTimeMillis() - tempMessageSentTime) > 60000) {//SEND_SMS_TIME_INTERVAL) {
                    for (UserInfo uinfo : UserConfiguration.getInstance().getTeammates()) {
                        String message = "Warning! Your athlete's current heart rate is " + mAntManager.getBPM() + " bpm.";
                        String mobilePhone = uinfo.getMobilePhone();
                        Utility.debug(this.getClass().getName(), "sending sms to " + uinfo.getMobilePhone());
                        /*SmsManager smsManager = SmsManager.getDefault();
                            smsManager.sendTextMessage(mobilePhone, null, message, null, null);*/
                        sendHeartRateToServer();
                    }
                    tempMessageSentTime = System.currentTimeMillis();
                }
            }
        } catch (Exception e) {
            Utility.error(this.getClass().getName(), "Error in CheckAndNotifyMaxHeartRate() method." + e.getMessage());
        }
    }

    /* Handler antConnectionHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.getData().get("message").equals("ant enabled")) {
                if (!mAntManager.isChannelOpen(AntPlusManager.HRM_CHANNEL)) {
                    Utility.debug(this.getClass().getName(), "onClick (HRM): Open channel");
                    mAntManager.openChannel(AntPlusManager.HRM_CHANNEL, false);
//                    ((Button)findViewById(R.id.btn_enable_ant)).setText(getString(R.string.Disable_Ant));
                }
            } else {
            }
        }
    };*/
}
